package pers.amos.dubbo.rpc;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * @author amos wong
 * @create 2020-12-07 9:03 下午
 */

public class AmosRpcFramework {

    /**
     * 服务暴露的接口
     * 提供方需要将自己的接口暴露出去 供消费者调用
     *
     * @param service 需要暴露的服务实例
     * @param port    暴露的接口
     */
    public void export(Object service, int port) {
        try {
            ServerSocket serverSocket = new ServerSocket(port);

            while (true) {
                // 接受 socket 连接
                Socket socket = serverSocket.accept();

                new Thread(() -> {
                    try {
                        ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());

                        String methodName = (String) inputStream.readObject();
                        Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();
                        Object[] arguments = (Object[]) inputStream.readObject();
                        Method method = service.getClass().getMethod(methodName, parameterTypes);

                        // 调用方法
                        Object result = method.invoke(service, arguments);

                        // 返回结果 序列化
                        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                        output.writeObject(result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static <T> T refer(Class<T> interfaceClass, String host, int port) {
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket(host, port);

                // 传递要调用的方法的信息 -序列化
                try {
                    ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());

                    outputStream.write(method.getName().getBytes(StandardCharsets.UTF_8));
                    // 写入方法的参数类型
                    outputStream.write("".getBytes(StandardCharsets.UTF_8));
                    outputStream.writeObject(method.getParameters());  //传参数值

                    // 等待返回结果
                    ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                    Object result = inputStream.readObject();
                    return result;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }
}
